#!/usr/bin/env python
#coding=utf-8

import json
import datetime
import time
from pprint import pprint
from sqlalchemy.orm import Session
# from datetime import date, datetime, timedelta


from app.bill.models.bill_instance import BillInstance
from app.celery_task.models.celery_task import CeleryTask
from app.bill.models.tag_department import TagDepartment
from app.bill.utils.aliyun_bill_utils import AliyunBillUtils
from app.bill.utils.huaweiyun_bill_utils import HuaweiyunBillUtils
from app.bill.utils.tag_department_utils import TagDepartmentUtils
from app.bill.handlers.bill_month_handler import BillMonthHandler
from app.bill.handlers.bill_analysis_handler import BillAnalysisHandler
from app.service_quality.handlers.qos_server_info_handler import QosServerInfoHandler


# from common.sa_platform import SaPlatform
from core.logger import logger
from setting import config

class BillSyncUtils(object):
    def __init__(self):
        # self.sa_platform = SaPlatform()
        # sa 账号信息
        self.sa_access_key = config.SA_ACCESS_KEY
        self.sa_access_secret = config.SA_ACCESS_SECRET

        # tianmian账号信息
        self.tianmian_access_key = config.TIANMIAN_ACCESS_KEY
        self.tianmian_access_secret = config.TIANMIAN_ACCESS_SECRET
        
        # 华为云tianmiantech账号信息
        self.tianmiantech_access_key = config.TIANMIANTECH_ACCESS_KEY
        self.tianmiantech_access_secret = config.TIANMIANTECH_ACCESS_SECRET
        
        # 华为云welabcloud账号信息
        self.welabcloud_access_key = config.WELABCLOUD_ACCESS_KEY
        self.welabcloud_access_secret = config.WELABCLOUD_ACCESS_SECRET


    def task_begin(self, db: Session, bill_cycle: str):
        # 检查同步的账期是否合规, 每次月初就需要跑，先把判断去掉，每月7号有定时任务再跑一次
        # if not self.check_bill_cycle(bill_cycle):
        #     logger.error(f'task begin, bill_cycle:{bill_cycle} is invalid')
        #     return False

        # 启动异步任务
        from app.bill.task import bill_update_async
        # 判断任务是否已经启动，控制只有一个任务在跑。 如果任务可以多个同时进行，则无需这一步判断。
        task_name = bill_update_async.name.split(".")[-1]
        if CeleryTask.is_progress_on_name(db, task_name):
            logger.error(f"task: {task_name} was stared")
            return False

        task = bill_update_async.delay(bill_cycle)
        task_data = {
            "name": task_name,
            "tid": task.id,
            "state": task.state
        }

        logger.info(f"add task: {task_name} success")
        return task_data


    def task_handle(self, db: Session, tid: str, bill_cycle: str):
        """
        同步阿里云的账单
        """
        # 获取账期
        if not bill_cycle:
            # 定时任务，每月7号，同步上个月的账期
            last_month = datetime.date(datetime.date.today().year, datetime.date.today().month, 1) - datetime.timedelta(days=1)
            bill_cycle = last_month.strftime("%Y-%m")

        # 更新账单数据
        result = self.update_bill_instance(db, bill_cycle)
        if not result:
            logger.error(f"update bill instance:{bill_cycle} failed")
            return False

        comment = f"update bill instance:{bill_cycle} success"
        CeleryTask.update_task(db, tid, progress=30, comment=comment)
        logger.info(comment)


        # 对比一个月的差异账单，并写入数据
        bill_analysis_handler = BillAnalysisHandler()
        result = bill_analysis_handler.month_on_month(db, bill_cycle)
        if not result:
            logger.error(f"update analysis:{bill_cycle} failed")
            return False

        comment = f"update analysis:{bill_cycle} success"
        CeleryTask.update_task(db, tid, progress=60, comment=comment)
        logger.info(comment)


        # 同步月份信息表
        bill_month_handler = BillMonthHandler()
        result = bill_month_handler.update_from_instance(db, bill_cycle)
        if not result:
            logger.error(f"update month bill:{bill_cycle} failed")
            return False

        comment = f"update month bill:{bill_cycle} success"
        CeleryTask.update_task(db, tid, progress=90, comment=comment)
        logger.info(comment)

        return True



    def update_bill_instance(self, db: Session, bill_cycle: str):
        """
        同步阿里云账单到数据库
        """
        bill_datas = []
        welab_bill = AliyunBillUtils("welab", self.sa_access_key, self.sa_access_secret, 'cn-shenzhen')
        tianmian_bill = AliyunBillUtils("tianmian", self.tianmian_access_key, self.tianmian_access_secret, 'cn-shenzhen')
        tianmiantech_bill = HuaweiyunBillUtils("tianmiantech", self.tianmiantech_access_key, self.tianmiantech_access_secret)
        welabcloud_bill = HuaweiyunBillUtils("welabcloud", self.welabcloud_access_key, self.welabcloud_access_secret)
        


        # 获取卫盈联和天冕的账单
        bill_datas.extend(welab_bill.get_bill_to_database(bill_cycle))
        bill_datas.extend(tianmian_bill.get_bill_to_database(bill_cycle))
        bill_datas.extend(tianmiantech_bill.get_bill_to_database(bill_cycle))
        bill_datas.extend(welabcloud_bill.get_bill_to_database(bill_cycle))

        # 更新k8s-node节点的资源分账比例
        result = self.update_k8s_splite(db)
        if not result:
            logger.error("update k8s splite failed")
            return False

        # 删除数据库中已经存在的数据
        BillInstance.delete_cycle(db, bill_cycle)

        # 同步账单数据到数据库
        result = self.bill_instance_to_database(db, bill_datas)
        if not result:
            logger.error(f"bill instance to database failed")
            return False
            
        return True


    def bill_instance_to_database(self, db: Session, bill_data: list):
        """
        遍历每个账单，获取：公司 环境 管理部门 所属部门 分账入库
        """
        tag_department_utils = TagDepartmentUtils()
        for bill in bill_data:
            department_info = tag_department_utils.get_department_info(db, bill)
            bill["tag_key"] = "appname"
            bill["tag_value"] = tag_department_utils.get_tag_value(bill, bill["tag_key"])
            bill["company"] = tag_department_utils.get_company_name(db, bill)
            bill["bill_class"] = tag_department_utils.get_classes_name(db, bill)
            bill["environment"] = department_info["environment"]
            bill["maintain_department"] = department_info["maintain_department"]

            for belong_department, proportion in department_info["belong_department"].items():
                bill_copy = bill.copy()
                bill_copy["belong_department"] = belong_department
                bill_copy["pretax_gross_amount"] = bill["pretax_gross_amount"] *proportion/100
                bill_copy["invoice_discount"] = bill["invoice_discount"] * proportion / 100
                bill_copy["deducted_by_coupons"] = bill["deducted_by_coupons"] * proportion / 100
                bill_copy["pretax_amount"] = bill["pretax_amount"] * proportion / 100
                bill_copy["payment_amount"] = bill["payment_amount"] * proportion / 100
                bill_copy["deducted_by_cash_coupons"] = bill["deducted_by_cash_coupons"] * proportion / 100
                bill_copy["deducted_by_prepaid_card"] = bill["deducted_by_prepaid_card"] * proportion / 100
                bill_copy["outstanding_amount"] = bill["outstanding_amount"] * proportion / 100
                result = BillInstance.add(db, bill_copy)
                if not result:
                    logger.error(f"add bill instance to database failed, instance_id:{bill_copy['instance_id']}")
                    return False
                
        logger.info(f"add bill instance to database success")
        return True



    def update_k8s_splite(self, db: Session):
        # 从sa平台获取： 根据每个部门的服务容器的cpu总占比来分摊k8s-node节点的资产费用
        # department_info = self.sa_platform.get_department_ratio()
        # belong_department = {
        #     department_ratio["name"]: department_ratio["cpu_ratio"]
        #     for department_ratio in department_info["departs_info"]
        # }

        # 从本地数据中容器服务的cpu总占比，来分摊k8s-node节点的资产费用。
        qos_server_info_handler = QosServerInfoHandler()
        belong_department = qos_server_info_handler.get_department_ratio_for_container(db)
        if not belong_department:
            logger.error(f"get belong_department failed")
            return False

        # 更新数据库
        tag_department_object = TagDepartment.get_object_by_tag(db, "appname", "p_k8s-node节点")
        if tag_department_object:
            # 如果存在， 则更新
            tag_department_info = TagDepartment.get_object_info_by_tag(db, "appname", "p_k8s-node节点")
            tag_department_info["belong_department"] = belong_department
            result = TagDepartment.update(db, tag_department_info)
            if not result:
                logger.error(f"update tag department to database failed")
                return False
            logger.info(f"update tag department to database success, tag_department_info:{tag_department_info}")
        else:
            # 如果不存在， 则新增
            tag_department_info = {
                "tag_key": "appname",
                "tag_value": "p_k8s-node节点",
                "environment": "生产环境",
                "maintain_department": "运维",
                "belong_department": belong_department,
                "comment": "生产环境的k8s集群"
            }
            result = TagDepartment.add(db, tag_department_info)
            if not result:
                logger.error(f"add tag department to database failed")
                return False
            logger.info(f"add tag department to database success, tag_department_info:{tag_department_info}")
        return True


    def get_bill_instance_from_aliyun(self, bill_cycle):
        """
        直接从阿里云获取账单统计数据
        """

        welab_bill = AliyunBillUtils("welab", self.sa_access_key, self.sa_access_secret, 'cn-shenzhen')
        tianmian_bill = AliyunBillUtils("tianmian", self.tianmian_access_key, self.tianmian_access_secret, 'cn-shenzhen')

        # 获取账单信息
        welab_total, welab_data = welab_bill.count_account_by_pd(bill_cycle)
        tianmian_total, tianmian_data = tianmian_bill.count_account_by_pd(bill_cycle)

        #对结果进行处理
        data = []
        data.extend(welab_data)
        data.extend(tianmian_data)
        for count in data:
            count["PretaxAmount"] = round(float(count["PretaxAmount"]), 2)
        total = round(float(welab_total+tianmian_total), 2)

        return {
            "total": total,
            "data": data
        }


    def check_bill_cycle(self, bill_cycle):
        """
        检查账期是否合规
        """
        # 最早可同步的时间戳
        current_month_date = datetime.date.today() - datetime.timedelta(days=7) #月初4日后才能同步上月的账单
        last_month_date = datetime.datetime(current_month_date.year, current_month_date.month, 1) - datetime.timedelta(days=1)
        last_month_stamp = datetime.datetime.timestamp(last_month_date)

        # 参数的时间戳
        bill_cycle_date = datetime.datetime.strptime(bill_cycle, "%Y-%m")
        bill_cycle_stamp = datetime.datetime.timestamp(bill_cycle_date)

        if bill_cycle_stamp > last_month_stamp:
            # 参数指定的时间戳不能比最早可同步的时间戳早
            return False
        else:
            return True




def main():
    sync_bill = BillSyncUtils()
    sync_bill.check_bill_cycle("2021-12")



if __name__ == "__main__":
    main()
